﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;

namespace MassIntellect.WorkIt
{
	public class ServiceLocator : IServiceLocator, ISerialize
	{
		private class NullServiceLocator : IServiceLocator
		{
			#region IServiceLocator Members

			public bool IsCreateNewInstanceEveryResolve { get; set; }

			public bool IsAllowReregister { get; set; }

			public T Resolve<T>() where T : class
			{
				return null;
			}

			public T Resolve<T>(bool createNewInstance) where T : class
			{
				return null;
			}

			public bool Register<T, P>()
				where T : class
				where P : T, new()
			{
				return false;
			}

			public bool Register<T, P>(P instance)
				where T : class
				where P : T, new()
			{
				return false;
			}

			#endregion
		}
		public static readonly IServiceLocator NoService = new NullServiceLocator();

		private readonly Dictionary<Type, Type> services = new Dictionary<Type, Type>();
		private readonly Dictionary<Type, object> instances = new Dictionary<Type, object>();
		private readonly Dictionary<Type, XElement> series = new Dictionary<Type, XElement>();

		#region IServiceLocator Members

		[Serialize(SerializeAs.Attribute)]
		public bool IsAllowReregister { get; set; }

		[Serialize(SerializeAs.Attribute)]
		public bool IsCreateNewInstanceEveryResolve { get; set; }

		public T Resolve<T>() where T : class
		{
			return Resolve<T>(IsCreateNewInstanceEveryResolve);
		}

		public T Resolve<T>(bool createNewInstance) where T : class
		{
			Type typeService;
			T res = null;
			T newr = null;

			if (services.TryGetValue(typeof(T), out typeService))
			{
				XElement xe;
				if (createNewInstance)
				{
					
					newr = res = series.TryGetValue(typeof(T), out xe)
							? XmlSerializeProvider.Deserialize<T>(xe)
							: typeService.CreateInstance<T>();
				}
				else
					res = (T)instances.GetOrDefault(typeof(T), () =>
						newr = series.TryGetValue(typeof(T), out xe)
							? XmlSerializeProvider.Deserialize<T>(xe)
							: typeService.CreateInstance<T>());
				if (newr != null)
				{
					if (!IsCreateNewInstanceEveryResolve && !instances.ContainsKey(typeof(T)))
						instances.AddOrSetValue(typeof(T), newr);
					ServiceLocatorDependConnect(newr as IServiceLocatorDepend);
				}
			}
			return res;
		}

		private void ServiceLocatorDependConnect(IServiceLocatorDepend sld)
		{
			if (sld != null)
				sld.ServiceLocator = this;
		}

		public bool Register<T, P>()
			where T : class
			where P : T, new()
		{
			var res = !services.ContainsKey(typeof(T));
			if (res)
				services.Add(typeof(T), typeof(P));
			else if (res = IsAllowReregister)
				services[typeof(T)] = typeof(P);
			return res;
		}

		public bool Register<T, P>(P instance)
			where T : class
			where P : T, new()
		{
			bool res = Register<T, P>();
			if (res && instance != null)
				instances.AddOrSetValue(typeof(T), instance);
			return res;
		}

		#endregion

		#region ISerialize Members

		public void SetUp(XElement xe)
		{
			Type tp;
			XmlSerializeProvider.SetUp(this, xe);
			services.AddRange(
				xe.Elements("References").Elements("Reference")
					.Select(s => new KeyValuePair<string, string>((string)s.Attribute("interface"), (string)s.Attribute("class")))
					.Where(s => !s.Key.IsEmpty() && !s.Value.IsEmpty())
					.Select(s => new KeyValuePair<Type, Type>(tp = TypeExtension.GetTypeFromName(s.Key), TypeExtension.GetTypeFromNameCompatible(s.Value, null, tp))));
			series.AddRange(
				xe.Elements("Instances").Elements("Instance")
					.Select(s => new KeyValuePair<string, XElement>((string)s.Attribute("interface"), s.Elements().FirstOrDefault()))
					.Where(s => !s.Key.IsEmpty() && s.Value != null)
					.Select(s => new KeyValuePair<Type, XElement>(tp = TypeExtension.GetTypeFromName(s.Key), s.Value))
				);
			//var q = xe.Elements("Instances").Elements("Instance")
			//        .Select(s => new KeyValuePair<string, XElement>((string)s.Attribute("interface"), s.Elements().FirstOrDefault()))
			//        .Where(s => !s.Key.IsEmpty() && s.Value != null)
			//        .Select(s => new KeyValuePair<Type, object>(tp = TypeExtension.GetTypeFromName(s.Key), XmlSerializeProvider.Deserialize(s.Value, tp)));
			//foreach (var item in q.Select(s => s.Value))
			//    ServiceLocatorDependConnect(item as IServiceLocatorDepend);
			//instances.AddRange(q);
		}

		public XElement Serialize()
		{
			XElement xe = XmlSerializeProvider.SerializeOnlyPropertyAndType(this, false);
			if (services.Count > 0)
				xe.Add(new XElement("References",
							services
								.Select(s => new XElement("Reference",
									new XAttribute("interface", s.Key.GetGenericName()),
									new XAttribute("class", s.Value.GetGenericName())))));
			if (instances.Count > 0 || series.Count > 0)
				xe.Add(new XElement("Instances",
					instances
						.Select(s => new KeyValuePair<Type, ISerialize>(s.Key, s.Value as ISerialize))
						.Where(s => s.Value != null)
						.Select(s => new XElement("Instance", new XAttribute("interface", s.Key.GetGenericName()), s.Value.Serialize()))),
					series.Keys
						.Except(instances.Keys)
						.Select(s => new XElement("Instance", new XAttribute("interface", s.GetGenericName()), series[s])));

			return xe;
		}

		#endregion
	}
}
